/*
 * Copyright 2007. Mount Sinai Hospital, Toronto, Canada.
 * 
 * Licensed under the GNU Lesser General Public License, Version 2. You
 * can find a copy of the license at:
 * 
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
 * 
 * IN NO EVENT SHALL MOUNT SINAI HOSPITAL BE LIABLE TO ANY PARTY FOR DIRECT, 
 * INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST 
 * PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, 
 * EVEN IF MOUNT SINAI HOSPITAL HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 
 * DAMAGE.
 * 
 * MOUNT SINAI HOSPITAL SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND 
 * ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". 
 * MOUNT SINAI HOSPITAL HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, 
 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 
 */
package com.sinai.mshab.client.ui.aggregate;

import com.google.gwt.user.client.ui.CellPanel;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sinai.mshab.client.util.Answer;
import com.sinai.mshab.client.util.AnswerCollection;
import com.sinai.mshab.client.util.Util;
import com.sinai.mshab.client.ui.FormComponent;
import com.sinai.mshab.client.ui.Key;
import com.sinai.mshab.client.ui.basic.CloneNotifierButton;
import com.sinai.mshab.client.ui.basic.RemoveNotifierButton;

/**
 * A <code>VerticalComponentList</code> is a vertical list of
 * {@link FormComponent}s. <code>VerticalComponentList</code> allows adding
 * new components into any index and can contain a link beside rows that allow
 * users to duplicate the row.
 * 
 * @author Arthur Kalmenson
 */
public class VerticalComponentList extends AggregateFormComponent {

	/** The panel containing all the widgets */
	protected VerticalPanel vertical_panel;

	private Label description_label;

	private Label quantifier_label;

	private Label error_label;

	protected Answer rowsToDelete;

	Panel widget;

	/** This variable is needed for vpanel population algorithm */
	private int last_populated_index;

	/**
	 * Create an empty <code>VerticalComponentList</code>.
	 * 
	 * @param tableName
	 *            the name of the database table where the
	 *            <code>VerticalComponentList</code>'s information will be
	 *            stored.
	 */
	public VerticalComponentList(String tableName) {
		initializeWidget(tableName, null, null, null, true);
	}

	public VerticalComponentList(String tableName, boolean vertical) {
		initializeWidget(tableName, null, null, null, vertical);
	}

	public VerticalComponentList(String tableName, String description) {
		initializeWidget(tableName, description, null, null, true);
	}

	public VerticalComponentList(String tableName, String description,
			boolean vertical) {
		initializeWidget(tableName, description, null, null, vertical);
	}

	public VerticalComponentList(String tableName, String description,
			String quantifier) {
		initializeWidget(tableName, description, quantifier, null, true);
	}

	public VerticalComponentList(String tableName, String description,
			String quantifier, boolean vertical) {
		initializeWidget(tableName, description, quantifier, null, vertical);
	}

	public VerticalComponentList(String tableName, String description,
			String quantifier, String error_msg) {
		initializeWidget(tableName, description, quantifier, error_msg, true);
	}

	public VerticalComponentList(String tableName, String description,
			String quantifier, String error_msg, boolean vertical) {
		initializeWidget(tableName, description, quantifier, error_msg,
				vertical);
	}

	protected void initializeWidget(String tableName, String description,
			String quantifier, String error_msg, boolean vertical) {

		vertical_panel = new VerticalPanel();
		vertical_panel.setStyleName("vertical_panel");
		super.initializeWidget(vertical_panel);
		initialize(tableName, description, quantifier, error_msg,
				vertical_panel, vertical);
	}

	public void initialize(String tableName, String description,
			String quantifier, String error_msg, Panel w, boolean vertical) {
		this.tableName = tableName;
		this.description = description;
		this.quantifier = quantifier;
		this.error_msg = error_msg;

		// answer object to keep track of the rows to be deleted.
		rowsToDelete = new Answer();
		rowsToDelete.setTableName(tableName);
		rowsToDelete.setMergeable(false);

		// all our widgets go to the horizontal panel and add custom style names
		// for CSS to give custom rendering to these components
		CellPanel panel;
		if (description != null) {
			description_label = new Label(description);
			description_label.setStyleName("mshab-descriptionLabelVertical");
		}
		if (w != null) {
			widget = w;

			// set the proper alt
			widget.setTitle(description);
		}
		if (quantifier != null) {
			quantifier_label = new Label(quantifier);
			quantifier_label.setStyleName("quantifier_label");
		}
		if (error_label != null) {
			error_label = new Label(error_msg);
			error_label.setStyleName("error_label");

			// no error at creation time, so error label not visible.
			error_label.setVisible(false);
		}

		// the panel being used will depend on horizontal.
		if (!vertical) {
			panel = new HorizontalPanel();

			// panel containing other widgets
			if (description_label != null) {
				panel.add(description_label);
			}
			if (widget != null) {
				panel.add(widget);
			}
			if (quantifier_label != null) {
				panel.add(quantifier_label);
			}
			if (error_label != null) {
				panel.add(error_label);
			}
		} else {
			panel = new VerticalPanel();

			// panel containing other widgets
			if (description_label != null) {
				panel.add(description_label);
			}
			if (quantifier_label != null) {
				panel.add(quantifier_label);
			}
			if (error_label != null) {
				panel.add(error_label);
			}
			if (widget != null) {
				panel.add(widget);
			}
		}

		initWidget(panel);
	}

	/**
	 * Append <code>question</code> to the current
	 * <code>VerticalComponentList</code>. <code>question</code> is put at
	 * the bottom of the list.
	 * 
	 * @param question
	 *            the <code>QuestionComposite</code> to append.
	 */
	public void add(FormComponent question) {
		add(question, vertical_panel.getWidgetCount());
	}

	/**
	 * Appends a simple widget at the end.
	 * 
	 * @param widget
	 *            the widget to append.
	 */
	public void add(Widget widget) {
		vertical_panel.add(widget);
	}

	/**
	 * Inserts a simple widget into index.
	 * 
	 * @param widget
	 *            the index where to insert the widget.
	 * @param index
	 *            the widget to insert.
	 */
	public void insert(Widget widget, int index) {
		vertical_panel.insert(widget, index);
	}

	/**
	 * Add <code>question</code> to the current
	 * <code>VerticalComponentList</code> at index <code>index</code>.
	 * 
	 * @param question
	 *            the <code>QuestionComposite</code> to add.
	 * @param index
	 *            the index where <code>question</code> should be added.
	 */
	public void add(FormComponent question, int index) {

		/*
		 * this set's the ID of the added component to the index of this widget
		 * in this vlist, crucial for vlist population
		 */
		question.setPanelId(index);
		vertical_panel.insert(question, index);

		// add myself as an observer.
		question.addObserver(this);
	}

	/**
	 * Remove the <code>QuestionComposite</code> at index <code>index</code>.
	 * 
	 * @param index
	 *            the index of the objec to remove.
	 */
	public void remove(FormComponent question) {
		vertical_panel.remove(question);
	}

	/**
	 * Return the total number of widgets in this
	 * <code>VerticalComponentList</code>.
	 * 
	 * @return the total number of widgets in this
	 *         <code>VerticalComponentList</code>.
	 */
	public int size() {
		return vertical_panel.getWidgetCount();
	}

	/**
	 * Recieve a notification from contained component, clone it and add the
	 * clone one index below the <code>who</code>.
	 * 
	 * @param who
	 *            the <code>QuestionComposite</code> that notifies this
	 *            <code>VerticalComponentList</code>.
	 */
	public void receiveNotification(FormComponent who, FormComponent originator) {

		// figure out which component notified me
		int index = vertical_panel.getWidgetIndex(who);

		// if we are cloning via clone button OR populate() is calling this
		// method.
		if (!(originator instanceof RemoveNotifierButton)) {
			FormComponent cloned_question = (FormComponent) who.clone();

			// if we cloned an hpanel, add a remove button to it.

			if (who instanceof HorizontalComponentList) {
				HorizontalComponentList hp = (HorizontalComponentList) who;
				if (!hp.hasRemoveButtons()) {
					hp
							.addNotifyingComponent(new RemoveNotifierButton(
									"Remove"));
				}
			}

			cloned_question.addObserver(this);

			// the new id of clone should be in the same group.
			// ie: if orig's id is: 2.xxxxxxxxxxxxx
			// then cloned id must be: 2.yyyyyyyyyyy
			double id_of_orig = who.getPanelId();
			int id_group = (int) id_of_orig;
			String new_id_string = id_group + "."
					+ ((int) cloned_question.newPanelId());

			System.out.println("orig id : " + id_of_orig);
			System.out.println("id group : " + id_group);
			System.out.println("new id string : " + new_id_string);

			double final_id = Double.parseDouble(new_id_string);
			cloned_question.setPanelId(final_id);

			// inserting the cloned object.
			vertical_panel.insert(cloned_question, index + 1);
		}
		// if remove button was clicked
		else if (originator instanceof RemoveNotifierButton) {

			// remove the notifying item
			vertical_panel.remove(index);

			// create a key which identified the item, to ensure it get's
			// deleted from the database
			if (who.isPopulated()) {
				Key k = new Key();
				k.add("id", who.getPanelId() + "", this.tableName);
				rowsToDelete.addDeleteKey(k);
				System.out.println("Adding Key to be removed " + this.tableName
						+ " : id=" + who.getPanelId());
			}
		}
	}

	public void sendAnswer(DataReceiver receiver) {

		// tell all subwidgets to send their answers.
		for (int i = 0; i < vertical_panel.getWidgetCount(); i++) {

			Widget element = vertical_panel.getWidget(i);
			if (element instanceof FormComponent) {

				FormComponent fc = (FormComponent) element;
				fc.sendAnswer(receiver);
			}
		}

		// send our own answer containing the rows to be removed
		receiver.sendData(rowsToDelete);
	}

	public Object clone() {

		/*
		 * We create a new VerticalComponentList, cycle through vertical_panel's
		 * components, clone them and add them to the new VerticalComponentList.
		 */
		VerticalComponentList result = new VerticalComponentList(tableName);

		for (int i = 0; i < vertical_panel.getWidgetCount(); i++) {

			FormComponent question = (FormComponent) vertical_panel
					.getWidget(i);
			result.add((FormComponent) question.clone());
		}

		return result;
	}

	/**
	 * Populate this widget with FormData using an Answer object
	 * 
	 * @param answer
	 *            the answer object containing the data.
	 */
	public boolean populate(Answer answer) {
		boolean successfully_populated = false;
		String id = answer.get("id");

		// if the ID is null, user put in god-knows-what into this panel
		if (id == null) {

			// simply populate all elements in this vpanel with current answer.
			for (int i = 0; i < vertical_panel.getWidgetCount(); i++) {

				Widget element = vertical_panel.getWidget(i);
				if (element instanceof FormComponent) {
					boolean success = ((FormComponent) element)
							.populate(answer);
					if (success) {
						successfully_populated = true;
					}
				}
			}
		}
		// this panel contains hpanels. This assumes that all components are
		// going to be
		// fed with non-mergable answers.
		else {
			double id_double = Double.parseDouble(id);
			int index_of_dot = id.indexOf('.');
			int id_second_half = Integer.parseInt(id
					.substring(index_of_dot + 1));

			// if this is an id of an original non-cloned component
			if (index_of_dot == -1 || id_second_half == 0) {

				// locate component with this id and tell it to populate.
				for (int i = 0; i < vertical_panel.getWidgetCount(); i++) {
					Widget element = vertical_panel.getWidget(i);
					if (element instanceof FormComponent) {
						FormComponent current_widget = (FormComponent) element;

						// if the ids match, update the needed widget.
						if (current_widget.getPanelId() == id_double) {
							successfully_populated = current_widget
									.populate(answer);
							last_populated_index = i;
							break;
						}
					}
				}

			}
			// if this answer is for a cloned object
			else if (last_populated_index != -1) {

				Widget element = vertical_panel.getWidget(last_populated_index);
				if (element instanceof FormComponent) {
					// obtain a copy of the very last widget populated
					FormComponent right_widget = (FormComponent) element;

					// tell vpanel to clone it
					receiveNotification(right_widget, this);

					// update the last populated index by 1
					last_populated_index++;

					// tell newly populated widget to clone itself.
					successfully_populated = ((FormComponent) vertical_panel
							.getWidget(last_populated_index)).populate(answer);
				}
			}
		}

		return successfully_populated;
	}

	/**
	 * Populate this widget with FormData
	 */
	public boolean populate() {
		boolean successfully_populated = false;

		// retrieve the answer collection from form data.
		AnswerCollection ac = FormData.getAnswerCollection(tableName);

		// if the answer collection is not null, populate subcompnents with
		// items from it.
		if (ac != null) {
			// nothing has been populated yet so reset the index
			last_populated_index = -1;

			// walking through all answer objects in the answer collection
			for (int i = 0; i < ac.size(); i++) {
				boolean success = populate(ac.get(i));
				if (success) {
					successfully_populated = true;
				}
			}
		}
		// otherwise just tell subcomponents to populate themselves.
		else {
			// simply populate all elements in this vpanel with current answer.
			for (int i = 0; i < vertical_panel.getWidgetCount(); i++) {

				if (vertical_panel.getWidget(i) instanceof FormComponent) {
					boolean success = ((FormComponent) vertical_panel
							.getWidget(i)).populate();
					if (success) {
						successfully_populated = true;
					}
				}
			}
		}

		return successfully_populated;
	}

	public VerticalPanel toReport() {

		VerticalPanel report = Util.verticalPanelToPrint(vertical_panel);

		// insert the description label into the beginning.
		if (description != null) {
			Label descLabel = new Label(description);
			descLabel.setStyleName(description_label.getStyleName());
			report.insert(descLabel, 0);
		}

		// insert the quantifier at the end.
		if (quantifier != null) {
			Label quanLabel = new Label(description);
			quanLabel.setStyleName(quantifier_label.getStyleName());
			report.insert(quanLabel, report.getWidgetCount());
		}

		return report;
	}

	/**
	 * @see FormComponent#isEmpty()
	 */
	public boolean isEmpty() {

		boolean empty = true;

		// go through every widget and see if it's empty. If any are not empty,
		// we return
		// false.
		for (int i = 0; i < vertical_panel.getWidgetCount(); i++) {
			Widget w = vertical_panel.getWidget(i);
			if (w instanceof FormComponent) {
				FormComponent component = (FormComponent) w;
				if (!component.isEmpty()) {
					empty = false;
				}
			}
		}

		return empty;
	}

	/**
	 * Clears out everything in the vpanel
	 */
	public void clear() {
		vertical_panel.clear();
	}
}
